/*
  
  这是一个 1.69寸 TFT 显示屏 显示时间的项目

  -- 每秒刷新一次  -- 请求失败或Wi-Fi没连接则 什么也不做 - requestCount（当前回调的次数） = 0；
                ｜
                 --- 连接Wi-Fi成功 没有请求失败时 开始每秒计数  requestCount++ （当前回调的次数）;
                      --- 每天 23点59分59秒 requestCount（当前回调的次数） 清零


  loop函数 -- requestCount（当前回调的次数）== 0 时，刷新显示
          

*/

#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7789.h> // Hardware-specific library for ST7789
#include <SPI.h>

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266WiFiMulti.h>
#include <Ticker.h>
#include <time.h>

#include <ArduinoJson.h>

ESP8266WiFiMulti wifiMulti;

// WiFi connect timeout per AP. Increase when connecting takes longer.
const uint32_t connectTimeoutMs = 5000;

// tft display ---------------------------------------------------------
#if defined(ARDUINO_FEATHER_ESP32) // Feather Huzzah32
  #define TFT_CS         14
  #define TFT_RST        15
  #define TFT_DC         32

#elif defined(ESP8266)
  #define TFT_CS         4
  #define TFT_RST        16
  #define TFT_DC         5

#else
  // For the breakout board, you can use any 2 or 3 pins.
  // These pins will also work for the 1.8" TFT shield.
  #define TFT_CS        10
  #define TFT_RST        9 // Or set to -1 and connect to Arduino RESET pin
  #define TFT_DC         8
#endif

Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);

float p = 3.1415926;

// wifi ---------------------------------------------------------
#ifndef STASSID
#define STASSID "wifi-wjh6"
#define STAPSK "987987987"
// 测试环境 是否开启串口打印
#define SERIAL_P true //false // 
#define SERIAL_I true //false // 
#endif

const char* ssid = STASSID;
const char* password = STAPSK;

// 北京时间URL
const char* beijingTimeUrl = "https://bjtime.org.cn/bjtime.php?timeZone=8";
// 高德天气api 来自中国气象局 实况天气 每日300000次
const char* weatherUrl = "https://restapi.amap.com/v3/weather/weatherInfo?key=11792e0ede8799a1f8e67d0ac4fa96a9&city=110000&output=JSON&extensions=";

void getTimeInfo();
Ticker timer1; // 每秒回调计时器

int requestCount = 0; // 当前回调的次数
int httpCode = 0;     // 网络请求结果状态
int requestStatu = 0; // 网络请求状态

int year  = 0; 
int month = 0;
int day   = 0; 
int hour  = 0;
int minute = 0;
int second = 0;
int weekday = 0;
int beijingTimestamp = 0;


// 实况天气结构体
struct WeatherLives {
  String province;          //省份名
  String city;              //城市
  String adcode;            //区域编码
  String weather;           //天气现象
  String temperature;       //实时气温，单位：摄氏度
  String winddirection;     //风向描述
  String windpower;         //风力级别，单位：级
  String humidity;          //空气湿度
  String reporttime;        //数据发布的时间
  String temperature_float; //实时气温，单位：摄氏度
  String humidity_float;    //空气湿度
};

// 预报天气结构体
struct WeatherForecasts {
  String province;      //省份名
  String city;          //城市
  String adcode;        //区域编码
  String reporttime;    //数据发布的时间

  String date;            //日期
  String week;            //星期几
  String dayweather;      //白天天气现象
  String nightweather;    //晚上天气现象
  String daytemp;         //白天温度
  String nighttemp;       //晚上温度
  String daywind;         //白天风向
  String nightwind;       //晚上风向
  String daypower;        //白天风力
  String nightpower;      //晚上风力
  String daytemp_float;   //白天温度 float
  String nighttemp_float; //晚上温度 float
};

// 实况天气数据
struct WeatherLives wLive;
// 预报天气数据
struct WeatherForecasts wForecast[4];

// 实况天气请求次数
int liveCount = 0;
// 预报天气请求次数
int forecastCount = 0;

void setup() {
  // put your setup code here, to run once:
  if (SERIAL_I) {
    Serial.begin(9600);
  }
  
  initDisplay();
  initWifi();

  timer1.attach(1, refreshCount); // 每秒计数
}

void loop() {
  // put your main code here, to run repeatedly:


}

// 计时器出发的每秒计时
// 在 连接Wi-Fi成功 且 https请求成功 之后开始计数
void refreshCount() {

  if (requestCount == 0) {// 初次更新一次时间
      
      getTimeInfo();
  }

  //  -------- 处理请求计数 -----------
  // 请求成功或者失败都要定时更新时间戳
  beijingTimestamp++;
  timestamp_to_beijing(beijingTimestamp); // 赋值给时间参数
  if (requestStatu != -1 && WiFi.status() == WL_CONNECTED) { 
    // 连接Wi-Fi成功 没有请求失败时 开始每秒计数
    requestCount++;
  }else { // 请求失败或Wi-Fi没连接则
    requestCount = 0;
    tft.fillScreen(ST77XX_BLACK);
    return;
  }
  if (hour == 23 && minute == 59 && second == 59) { // 每天 23点59分59秒 重新计时
    requestCount = 0;
  }

  //  -------- 处理显示和数据信息 -----------
  if (second == 0) { // 每分钟第 第0秒 更新显示时间 -- 每分钟执行一次
      showTime();
      if (SERIAL_P) {
        Serial.println("每分钟更新显示时间\n\n");
      }
  }else if (second == 1 && minute == 55) { // 每小时的 第55分钟 第1秒 更新时间信息 -- 每小时执行一次
    getTimeInfo();
  }else if ( second == 1 && (minute == 30 || minute == 0) ) { // 每小时的 第0分钟 或 第30分钟 第1秒 更新天气信息  -- 每半小时执行一次
    //getWeatherData(1);// 实况
    //getWeatherData(2);// 预报
  }
}

void showTime() {

  int one = 4;
  int two = 5;

  tft.fillScreen(ST77XX_BLACK);
  // 年月日
  tft.setCursor(20, 2);
  displayPrintInt(year, one);
  displayPrint("-", one);
  if (month < 10) {
    displayPrintInt(0, two);
  }
  displayPrintInt(month, one);
  displayPrint("-", one);
  if (day < 10) {
    displayPrintInt(0, two);
  }
  displayPrintInt(day, one);
  displayPrintln(" ", one);
  //displayPrintInt(weekday, one);
  //displayPrintln(" ", one);

  // 时分
  tft.setCursor(70, 36);
  if (hour < 10) {
    displayPrintInt(0, two);
  }
  displayPrintInt(hour, two);
  displayPrint(":", two);
  if (minute < 10) {
    displayPrintInt(0, two);
  }
  displayPrintInt(minute, two);
  //displayPrint(":", two);
  //displayPrintInt(second, two);
  if (SERIAL_P) {
    Serial.println("更新时间显示");
  }
}

// 获取北京时间信息
void getTimeInfo() {
  String payload = getHttpsInfo(beijingTimeUrl);
  if (payload.length() == 0) {
    Serial.println("获取北京时间 请求失败\n\n");
    return;
  }
  // ------ 处理时间戳信息 ------
  // 定义要截取的起始位置和长度
  int start = 9; // 假设我们要开始截取的位置
  int length = 10; // 假设我们要截取的长度
  // 使用substring方法截取字符串
  String part = payload.substring(start, start + length);
  int timestamp = part.toInt();
  beijingTimestamp = timestamp;
  timestamp_to_beijing(timestamp);
  if (SERIAL_P) {
    Serial.println("payload begin");
    Serial.println(payload);
    Serial.println(part);
    Serial.println("payload end");
  }

  showTime();
  if (SERIAL_P) {
    Serial.println("获取北京时间后\n\n");
  }
  if (requestStatu > 0) {//请求成功
    
  }else { // 请求失败

  }
}

// 获取接口数据
// url 请求地址https
String getHttpsInfo(String url) {
  
  requestStatu = 0; // 开始请求
  logRequestStatu();
  String requestInfo = "http unable to connect";
  if ((wifiMulti.run() == WL_CONNECTED)) { // 连接Wi-Fi成功

    //创建 HTTPClient 对象
    HTTPClient httpsClient;
    BearSSL::WiFiClientSecure wifiClient;

    wifiClient.setInsecure();
    if (SERIAL_P) {
        Serial.println(">> http begin request");
    }

    if ( httpsClient.begin(wifiClient, url) ) {
      if (SERIAL_P) {
        Serial.println(">> http get request：" + url);
      }
      // 启动连接并发送HTTP头
      httpCode = httpsClient.GET();
      if (SERIAL_P) {
        Serial.println(">> http get request httpCode：" + httpCode);
      }
      requestStatu = 1; // 请求完成
      logRequestStatu();
      if (httpCode == 200) {
        requestStatu = 200; // 请求成功
        logRequestStatu();
      }
      if (SERIAL_P) {
          Serial.printf(">> http get code:%d\n", httpCode);
      }

      if (httpCode > 0) {

        if (httpCode == HTTP_CODE_OK) {
          String payload = httpsClient.getString();
          
          requestInfo = payload;
        }else {

          requestInfo = "http code not ok";
        }
        if (SERIAL_P) {
          Serial.println(requestInfo);
          Serial.println(requestStatu);
          Serial.println(httpCode);
        }
        // 返回payload
        return requestInfo;
      }else {
        if (SERIAL_P) {
          Serial.printf("http get failed, error:%s\n", httpsClient.errorToString(httpCode).c_str());
          Serial.println("");
        }
      }
      httpsClient.end();
      requestStatu = 5;// 请求结束
      logRequestStatu();
    }else {
      requestStatu = -1;//请求失败
      logRequestStatu();
      if (SERIAL_P) {
        Serial.println("http unable to connect");
      }
    }
  }else {
    requestInfo = "wifi is not connected";
    requestStatu = -2; // 网络未链接
    httpCode = -2;// 网络未链接
  }
  if (SERIAL_P) {
    Serial.println("http end request info");
    Serial.println(requestInfo);
    Serial.println(requestStatu);
    Serial.println(httpCode);
  }
  return "";//requestStatu;
}

void logRequestStatu() {
  if (SERIAL_I) {
    Serial.print("网络请求状态：");
    Serial.println(requestStatu == 0 ? "开始请求" : requestStatu == -1 ? "请求失败" : requestStatu == 200 ? "请求成功" : requestStatu == 1 ? "请求完成" : requestStatu == 5 ? "请求结束" : "未知状态");
  }
}

// 初始化TFT显示
void initDisplay() {
// use this initializer (uncomment) if using a 1.69" 280x240 TFT:
  tft.init(240, 280);           // Init ST7789 280x240
  tft.setRotation(3);           // 旋转屏幕至合适的方向
  // SPI speed defaults to SPI_DEFAULT_FREQ defined in the library, you can override it here
  // Note that speed allowable depends on chip and quality of wiring, if you go too fast, you
  // may end up with a black screen some times, or all the time.
  tft.setSPISpeed(40000000);
  if (SERIAL_P) {
    Serial.println(F("Initialized"));
  }

  uint16_t time = millis();
  tft.fillScreen(ST77XX_BLACK);
  time = millis() - time;
  if (SERIAL_P) {
    Serial.println(time, DEC);
  }
  delay(500);

  tft.setTextWrap(false);
  tft.fillScreen(ST77XX_BLACK);
  tft.setCursor(0, 25);
  displayPrintln("init display", 2);
}
// 初始化Wi-Fi
void initWifi() {

  displayPrintln("Connecting", 2);
  WiFi.mode(WIFI_STA);
  // Register multi WiFi networks
  wifiMulti.addAP("ChinaNet-QbGx", "esfhkwihzd638h");
  wifiMulti.addAP("wifi-wjh6", "987987987");
  wifiMulti.addAP("iPhone X", "987987987");
  
  while (wifiMulti.run(connectTimeoutMs) != WL_CONNECTED) {
    delay(500);
    displayPrint(".", 1);
  }

  displayPrintln(" ", 1);
  displayPrintln("WiFi connected: ", 2);
  displayPrintln(WiFi.SSID().c_str(), 2);
  displayPrintln("IP address: ", 2);
  displayPrintln(WiFi.localIP().toString().c_str(), 2); 
}

// 换行打印
void displayPrintlnPro(const char str[], int size, int corx, int cory) {
  tft.setTextColor(ST77XX_WHITE);
  tft.setCursor(corx, cory);
  tft.setTextSize(size);
  tft.println(str);
  if (SERIAL_P) {
    Serial.println(str);  
  }
}

// 换行打印
void displayPrintln(const char str[], int size) {
  tft.setTextColor(ST77XX_WHITE);
  tft.setTextSize(size);
  tft.println(str);
  if (SERIAL_P) {
    Serial.println(str);  
  }
}
// 打印
void displayPrint(const char str[], int size) {
  tft.setTextColor(ST77XX_WHITE);
  tft.setTextSize(size);
  tft.print(str);
  if (SERIAL_P) {
    Serial.print(str);  
  }
}
// 打印
void displayPrintInt(int str, int size) {
  tft.setTextColor(ST77XX_WHITE);
  tft.setTextSize(size);
  tft.print(str);
  if (SERIAL_P) {
    Serial.print(str);  
  }
}
// 时间戳转换北京时间
void timestamp_to_beijing(int timestamp) {
 
  timestamp += 8 * 3600 + 10; // 加上8小时的秒数（8 * 3600 = 28800）
  time_t t = timestamp;
  // 将时间戳转换为tm结构
  struct tm *timeinfo;
  timeinfo = localtime(&t);

  year = timeinfo->tm_year + 1900;
  month = timeinfo->tm_mon + 1;
  day = timeinfo->tm_mday;
  hour = timeinfo->tm_hour;
  minute = timeinfo->tm_min;
  second = timeinfo->tm_sec;
  weekday = timeinfo->tm_wday;
  if (SERIAL_P) {
    Serial.println(""); 
    Serial.print(year);
    Serial.print("-");
    Serial.print(month);
    Serial.print("-");
    Serial.print(day);
    Serial.print(" ");
    Serial.print(hour);
    Serial.print(":");
    Serial.print(minute);
    Serial.print(":");
    Serial.print(second);

  }
}
